﻿#ifndef __INC_QGADPT_H__
#define __INC_QGADPT_H__

#ifdef _MSC_VER
#pragma once
#endif

#include "qgutypes.h"

//////////////////////////////////////////////////////////////////////////
// depict
class KAPI qgDpct : public qgObj
{
	KCH_SUBCLASS(qgDpct, qgObj);

public:
	struct Link
	{
		qgDpct*			parent;
		qgDpct*			next;
		qgDpct*			prev;

		ksize_t			sub_count;
		qgDpct*			sub_frst;
		qgDpct*			sub_last;
	};

protected:
	char				_name[64];
	ksize_t				_hashcode;

	kint				_id;

	bool				_visible;
	bool				_testtype;
	
	bool				_updated;
	bool				_rendered;

	tpl::Mat4			_calc;
	tpl::Mat4			_local;
	tpl::TrFm			_trfm;

	tpl::Vec3			_bmin;
	tpl::Vec3			_bmax;
	tpl::Vec3			_bctr;
	float				_brad;

	// cull type
	// distance

	Link				_link;

protected:
	qgDpct();
	~qgDpct() {};

public:
	virtual bool Update(float adv);
	virtual void Draw();

	virtual void SetLocation(const tpl::Vec3& v);
	virtual void SetRotation(const tpl::Quat& q);
	virtual void SetScaling(const tpl::Vec3& v);

	const char* GetName() { return _name; }
	ksize_t GetHashCode() { return _hashcode; }
	kint GetId() { return _id; }
	bool IsVisible() { return _visible; }
	bool IsTestType() { return _testtype; }
	bool IsUpdated() { return _updated; }
	bool IsRendered() { return _rendered; }
	const tpl::Mat4& GetCalcTM() { return _calc; }
	const tpl::Mat4& GetLocalTM() { return _local; }
	const tpl::TrFm& GetTrfm() { return _trfm; }
	const tpl::Vec3& GetLocation() { return _trfm.loc; }
	const tpl::Quat& GetRotation() { return _trfm.rot; }
	const tpl::Vec3& GetScaling() { return _trfm.scl; }
	const tpl::Vec3& GetBoundMin() { return _bmin; }
	const tpl::Vec3& GetBoundMax() { return _bmax; }
	const tpl::Vec3& GetBoundCenter() { return _bctr; }
	float GetBoundRadius() { return _brad; }

	void SetName(const char* name);
	void SetId(kint id) { _id = id; }
	void SetCalcTM(const tpl::Mat4& tm);
	void SetLocalTM(const tpl::Mat4 tm);
	void SetVisible(bool v) { _visible = v; }
	void SetTestType(bool v) { _testtype = v; }
	void SetUpdated(bool v) { _updated = v; }
	void SetRendered(bool v) { _rendered = v; }

	void NeedUpdate() { SetUpdated(false); }
	void NeedRender() { SetRendered(false); }

	qgDpct* GetParent() { return _link.parent; }
	kint GetSubCount() { return _link.sub_count; }
	bool AddSub(qgDpct* node);
	bool RemoveSub(qgDpct* node, bool checkall = true);
	void ClearSub();
	qgDpct* GetSub(const char* name);
	qgDpct* FindSub(kint id);

	bool UpdateTM();
	void UpdateSub(float adv);
};


//////////////////////////////////////////////////////////////////////////
// ray
class KAPI qgRayCast : public qgObj
{
	KCH_SUBCLASS(qgRayCast, qgObj);
	KCH_FRIEND(qgCamera);

protected:
	tpl::Vec3			_dir;
	tpl::Vec3			_org;
	tpl::Vec2			_loc;

	qgCamera*			_cam;

protected:
	qgRayCast();
	~qgRayCast();

public:
	bool SetPoint(float x, float y);
	bool SetPoint(const tpl::Quad& viewbound, float x, float y);

	const tpl::Vec3& GetDirection() { return _dir; }
	const tpl::Vec3& GetOrigin() { return _org; }

	tpl::Vec3 GetLocation(float dist);
	tpl::Line3 GetLine(float scale);

	bool IntersectTri(const tpl::Vec3& v0, const tpl::Vec3& v1, const tpl::Vec3& v2, float* distance = NULL);
};


//////////////////////////////////////////////////////////////////////////
// camera

//
struct qbParamProj
{
	float				fov;
	float				aspect;
	tpl::Vec2			z;
};

// 
struct qbParamView
{
	tpl::Vec3			at;
	tpl::Vec3			up;
	tpl::Vec3			ah;
};

//
struct qbParamCamera
{
	tpl::Mat4			proj;
	tpl::Mat4			view;

	tpl::Mat4			invv;
	tpl::Mat4			vipr;
};

//
class KAPI qgCamera : public qgDpct
{
	KCH_SUBCLASS(qgCamera, qgDpct);

public:
	struct Inst
	{
		bool			layout : 1;
		bool			stt : 1;
		kuint			reserved_flag : 6;

		bool			fps : 1;
		bool			maya : 1;
		kuint			reserved_inst : 6;

		bool			can_fly : 1;
		bool			can_turn : 1;
		kuint			reserved_extend : 14;
	};

protected:
	Inst				_insts;

	qbParamCamera		_camp;
	qbParamProj			_proj;
	qbParamView			_view;

protected:
	qgCamera();
	~qgCamera();

public:
	K_OVR bool Update(float adv);

	void SetProj(float fov, float znear, float zfar);
	void SetProj(float aspect, float fov, float znear, float zfar);

	void SetView(const tpl::Vec3& at, const tpl::Vec3& ahead);
	void SetViewPos(const tpl::Vec3& at);
	void SetViewAhead(const tpl::Vec3& ahead);

	const qbParamCamera& GetParamCamera() { return _camp; }
	const qbParamProj& GetParamProj() { return _proj; }
	const qbParamView& GetParamView() { return _view; }

	const tpl::Mat4& GetProjTM() { return _camp.proj; }
	const tpl::Mat4& GetViewTM() { return _camp.view; }
	const tpl::Mat4& GetViPrTM() { return _camp.vipr; }
	const tpl::Mat4& GetInvViewTM() { return _camp.invv; }

	float GetProjAspect() { return _proj.aspect; }
	float GetProjFov() { return _proj.fov; }
	float GetProjMinZ() { return _proj.z.min; }
	float GetProjMaxZ() { return _proj.z.max; }
	const tpl::Vec2& GetProjZ() { return _proj.z; }

	const tpl::Vec3& GetViewUp() { return _view.up; }
	const tpl::Vec3& GetViewPos() { return _view.at; }
	const tpl::Vec3& GetViewAhead() { return _view.ah; }

	void SetLayoutWork(bool iswork);
	float GetDistanceSq(const tpl::Vec3& v);
	float GetDistance(const tpl::Vec3& v);

	tpl::Vec3 Project(const tpl::Vec3& v, const tpl::Mat4& world);

	qgRayCast* CreateRayCast();

public:
	static qgCamera* New();
};


//////////////////////////////////////////////////////////////////////////
// FPS camera

enum qgFpsWhere
{
	QSFPSWMV_FORWARD = 0,
	QSFPSWMV_BACKWARD = 1,
	QSFPSWMV_LEFT = 2,
	QSFPSWMV_RIGHT = 3,
	QSFPSWMV_UP = 4,
	QSFPSWMV_DOWN = 5,
	QSFPSWTN_LEFT = 6,
	QSFPSWTN_RIGHT = 7,
	QSFPSWTN_UP = 8,
	QSFPSWTN_DOWN = 9
};

#define QSFPSA_MOVE_FORWARD			HB_BIT(QSFPSWMV_FORWARD)
#define QSFPSA_MOVE_BACKWARD		HB_BIT(QSFPSWMV_BACKWARD)
#define QSFPSA_MOVE_LEFT			HB_BIT(QSFPSWMV_LEFT)
#define QSFPSA_MOVE_RIGHT			HB_BIT(QSFPSWMV_RIGHT)
#define QSFPSA_MOVE_UP				HB_BIT(QSFPSWMV_UP)
#define QSFPSA_MOVE_DOWN			HB_BIT(QSFPSWMV_DOWN)
#define QSFPSA_TURN_LEFT			HB_BIT(QSFPSWTN_LEFT)
#define QSFPSA_TURN_RIGHT			HB_BIT(QSFPSWTN_RIGHT)
#define QSFPSA_TURN_UP				HB_BIT(QSFPSWTN_UP)
#define QSFPSA_TURN_DOWN			HB_BIT(QSFPSWTN_DOWN)

class KAPI qgFpsCamera : public qgCamera
{
	KCH_SUBCLASS(qgFpsCamera, qgCamera);

protected:
	kint				_ables;

	float				_spdmov;
	float				_spdang;

	tpl::Mat4			_rot;
	tpl::Vec3			_vrot;
	tpl::Vec3			_mov;
	tpl::Vec3			_ang;

	tpl::Vec3			_lmov;
	tpl::Vec3			_lang;

	tpl::Vec2			_pt[2];
	kbyte				_keys[24];

protected:
	qgFpsCamera();
	~qgFpsCamera();

public:
	K_OVR bool Update(float adv);
	K_OVR void SetRotation(const tpl::Quat& q);

	void SetRotation(const tpl::Vec3& v);
	void RefreshAngle(const tpl::Vec3& ang, float delta);
	void RefreshAngle(float delta);
	void RefreshMove(const tpl::Vec3& move, float delta);
	void RefreshMove(float delta);

	float GetMoveSpeed() { return _spdmov; }
	float GetAngleSpeed() { return _spdang; }
	bool CanFly() { return _insts.can_fly; }

	void SetFly(bool v);
	void SetMoveSpeed(float v);
	void SetAngleSpeed(float v);
	void SetKey(qgFpsWhere w, kbyte k);

	void MovePoint(float x, float y, float adv);
	void MoveKey(qgFpsWhere w);

public:
	static qgFpsCamera* New();
};


//////////////////////////////////////////////////////////////////////////
// maya camera
class KAPI qgMayaCamera : public qgCamera
{
	KCH_SUBCLASS(qgMayaCamera, qgCamera);

public:
	float				_dist;

	float				_spdmov;
	float				_spdang;

	tpl::Mat4			_rot;
	tpl::Vec3			_vrot;
	tpl::Vec3			_mov;
	tpl::Vec3			_ang;

	tpl::Vec3			_lmov;
	tpl::Vec3			_lang;

	tpl::Vec2			_pt[2];
	kbyte				_keys[24];

protected:
	qgMayaCamera();
	~qgMayaCamera();

public:
	K_OVR bool Update(float adv);
	K_OVR void SetRotation(const tpl::Quat& q);

	void SetRotation(const tpl::Vec3& v);

	void RefreshAngle(const tpl::Vec3& angle, float delta);
	void RefreshAngle(float delta);

	void RefreshMove(const tpl::Vec3& move, float delta);
	void RefreshMove(float delta);

	float GetMoveSpeed() { return _spdmov; }
	float GetAngleSpeed() { return _spdang; }
	float GetFar() { return _dist; }
	const tpl::Vec3& GetRotationVec() { return _vrot; }

	void SetFar(float dist) { _dist = dist; }
	void SetMoveSpeed(float v);
	void SetAngleSpeed(float v);
	void SetKey(qgFpsWhere w, kbyte k);

	void MovePoint(float x, float y, float adv);
	void MoveKey(qgFpsWhere w);

public:
	static qgMayaCamera* New();
};


#if 0
//////////////////////////////////////////////////////////////////////////
// SMM

class qgSmm;

//
enum qgSmmFrameType
{
	QGSMMFRM_DUMMY,
	QGSMMFRM_BONE,
	QGSMMFRM_MESH,
};

//
enum qgSmmAnimType
{
	QGSMMANM_UNIT,
	QGSMMANM_DATA,
};

//
struct qbSmmDrawData : public tpl::MemAlloc<true>
{
	kint				faces;
	kint				indexes;
	kint				vertices;

	tpl::vector<kint>			_i;
	tpl::vector<tpl::Vec3>		_v;
	tpl::vector<tpl::Vec3>		_n;
	tpl::vector<tpl::Vec2>		_t;
	tpl::vector<kuint>			_d;
	tpl::vector<tpl::Vec3>		_b;
	tpl::vector<tpl::Vec3>		_a;
	tpl::vector<qbVertexWeight>	_w;
};

// frame
class KAPI qgSmmFrame : public qgDpct
{
	KCH_SUBCLASS(qgSmmFrame, qgDpct);
	KCH_FRIEND(qgSmmCreator);

protected:
	qgSmmFrameType		_frmtype;
	kint				_insts;

	kint				_pid;
	kint				_eid;

	float				_len;
	tpl::Mat4			_defm;
	tpl::TrFm			_orgm;

protected:
	qgSmmFrame(qgSmmFrameType frmtype = QGSMMFRM_DUMMY);
	~qgSmmFrame() {};

public:
	qgSmmFrameType GetFrameType() { return _frmtype; }
	kint GetInsts() { return _insts; }
	kint GetPid() { return _pid; }
	const tpl::Mat4& GetDefTM() { return _defm; }
	const tpl::TrFm& GetOrgTM() { return _orgm; }
	float GetLength() { return _len; }
	bool IsAnim() { return !K_OFLAG(_insts, 0x1000); }

	void SetPid(kint pid) { _pid = pid; }
	void SetEId(kint exid) { _eid = exid; }
	void TurnAnim(bool value) { K_UFLAG(&_insts, 0x1000, !value); }
};

KT_BASEPTR(qgSmmFrame);

// bone
class KAPI qgSmmBone : public qgSmmFrame
{
	KCH_SUBCLASS(qgSmmBone, qgSmmFrame);
	KCH_FRIEND(qgSmmCreator);
	KCH_FRIEND(qgSmmSkin);

protected:
	tpl::Mat4			_subm;
	tpl::Mat4			_cmbn;

protected:
	qgSmmBone(qgSmmFrameType frmtype = QGSMMFRM_BONE);
	~qgSmmBone() {};

public:
	K_OVR bool Update(float adv);

	kint GetBoneId() { return _eid; }
	const tpl::Mat4& GetCombineTM() { return _cmbn; }
	const tpl::Mat4& GetSubTM() { return _subm; }

	void SetBoneId(kint bid) { _eid = bid; }
	void SetCombineTM(const tpl::Mat4& m) { _cmbn = m; }
	void SetSubTM(const tpl::Mat4& m) { _subm = m; }
};

KT_BASEPTR(qgSmmBone);

// mesh
class KAPI qgSmmMesh : public qgSmmFrame
{
	KCH_SUBCLASS(qgSmmMesh, qgSmmFrame);
	KCH_FRIEND(qgSmmCreator);

protected:
	qbSmmDrawData*		_data;

	qgVertexLayout*	_lo;
	qgBuffer*			_ib;
	qgBuffer*			_vdraw;
	qgBuffer*			_vnorm;
	qgBuffer*			_vweight;

	kint				_faces;
	kint				_indexes;

	qgTexture*			_tex[3];
	qgSplr*				_splr;
	qgRasz*				_rasz;
	qgBlnd*				_blnd;
	qgShader*			_shd;

protected:
	qgSmmMesh(qgSmmFrameType frmtype = QGSMMFRM_MESH);
	~qgSmmMesh();

public:
	K_OVR bool Update(float adv);
	K_OVR void Draw();

	void DrawOnly();

	kint GetResId() { return _eid; }

	void SetResId(kint id) { _eid = id; }

	//
	qgTexture* GetTexture(kint nth) { return _tex[nth]; }
	void SetTexture(kint nth, qgTexture* tex) { _tex[nth] = tex; }
	void SetSplr(qgSplr* splr) { _splr = splr; }
	void SetRasz(qgRasz* rasz) { _rasz = rasz; }
	void SetBlnd(qgBlnd* blnd) { _blnd = blnd; }
	void SetShader(qgShader* shd, qgVertexLayout* lo) { _shd = shd; _lo = lo; }

protected:
	virtual void _GenerateMesh(qgSmm* smm);
};

KT_BASEPTR(qgSmmMesh);

// skin
class KAPI qgSmmSkin : public qgSmmMesh
{
	KCH_SUBCLASS(qgSmmSkin, qgSmmMesh);
	KCH_FRIEND(qgSmmCreator);

protected:
	kint				_cmbns;
	tpl::Mat4*			_pcmbn;
	tpl::Mat4**			_pinds;

protected:
	qgSmmSkin(qgSmmFrameType frmtype = QGSMMFRM_MESH);
	~qgSmmSkin();

public:
	K_OVR bool Update(float adv);
	K_OVR void Draw();

	void DrawOnly();

protected:
	K_OVR void _GenerateMesh(qgSmm* smm);

public:
	void _BuildCombine(kint count);
};

KT_BASEPTR(qgSmmSkin);

// smm
class KAPI qgSmm : public qgSmmBone
{
	KCH_SUBCLASS(qgSmm, qgSmmBone);

public:
	typedef tpl::vector<kint>			int_array;
	typedef tpl::vector<qgTexture*>		texture_array;
	typedef tpl::vector<qgSmmFrame*>	dummy_array;
	typedef tpl::vector<qgSmmBone*>		bone_array;
	typedef tpl::vector<qgSmmMesh*>		mesh_array;
	typedef tpl::vector<qgSmmFrame*>	frame_array;

protected:
	qgSmmFrame*			_pivot;

	int_array			_mtfs;
	texture_array		_texs;
	dummy_array			_dmms;
	bone_array			_bons;
	mesh_array			_mshs;
	frame_array			_frms;

	kint				_dcnt;

protected:
	qgSmm();
	~qgSmm();

public:
	K_OVR bool Update(float adv);
	K_OVR void Draw();

	kint UpdateFrame(float adv);
	void DrawOnly();

	qgSmmFrameRef GetFrame(const char* name);
	qgSmmFrameRef FindFrame(kint id);

	kint GetMaterialFlagNth(kint nth);
	qgTexture* GetTextureNth(kint nth);
	qgSmmFrameRef GetFrameNth(kint nth);
	qgSmmFrameRef GetDummyNth(kint nth);
	qgSmmBoneRef GetBoneNth(kint nth);
	qgSmmMeshRef GetMeshNth(kint nth);

	kint GetTextureCount() { return _texs.size(); };
	kint GetFrameCount() { return _frms.size(); };
	kint GetDummyCount() { return _dmms.size(); };
	kint GetBoneCount() { return _bons.size(); };
	kint GetMeshCount() { return _mshs.size(); };

public:
	static qgSmm* New(kint id, const char* filename, kint flags);
};

//
struct qbTimeVec3
{
	float				t;
	tpl::Vec3			v;
};

//
struct qbTimeQuat
{
	float				t;
	tpl::Quat			q;
};

//
struct qbSmaKey : tpl::MemAlloc<true>
{
	int					ref;

	kuint				hash;
	char				name[64];

	kint				id;
	kint				pid;
	kint				bid;
	float				len;

	tpl::vector<qbTimeVec3>	locs;
	tpl::vector<qbTimeQuat>	rots;
	tpl::vector<qbTimeVec3>	scls;
};

//
struct qbSmaUnit : tpl::MemAlloc<true>
{
	int					index;

	kuint				hash;
	char				name[64];

	tpl::Vec2			dur;
	float				last;
	float				speed;
	float				cur;

	kint				type;
	kint				cmpr;

	tpl::vector<qbSmaKey*>		keys;
	tpl::vector<qgSmmFrame*>	links;

	qgSmm*				smm;

	void _UpdatePosition(float adv);
};

//
class KAPI qgSma : public qgObj
{
	KCH_SUBCLASS(qgSma, qgObj);

protected:
	char				_name[64];

	kushort				_version;
	kushort				_type;

	kint				_flags;

	bool				_isblend;
	bool				_isloop;
	bool				_isrev;

	float				_speed;

	tpl::vector<qbSmaUnit*>	_units;
	qbSmaUnit*			_cur;

	qgSmm*				_smm;

protected:
	qgSma();
	~qgSma();

public:
	void Link(qgSmm* smm);

	bool IsAnimate();
	bool IsFinish();

	bool SetUnit(const char* name);
	bool SetUnitNth(kint n);
	void SetSpeed(float f);
	void TurnLoop(bool value);

	bool Update(float adv);

	qbSmaUnit* GetUnit() { return _cur; }

private:
	bool _PrivateRead520(const kbyte* data, kint size, kint flags);

public:
	static qgSma* New(const char* filename, kint flags);
};
#endif

#endif	// __INC_QGADPT_H__
